package yl.hs.bmipfull.utils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import yl.hs.bmipfull.annotations.DBField;
import yl.hs.bmipfull.annotations.DBTable;
import yl.hs.bmipfull.pojo.IParent;
import yl.hs.bmipfull.pojo.material.RawMaterialInfoEntity;
import yl.hs.bmipfull.pojo.system.AreaEntity;
import yl.hs.bmipfull.viewmodels.OptionModel;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;


/***
 * @author 华沙信息
 *
 */
public class Tl {
	/**
	 * 判断字符串是否为空
	 *
	 * @param source 输入的字符串
	 * @return 判断结果
	 */
	public static boolean isEmpty(String source) {
		return source == null || "".equals(source.trim());
	}

	/**
	 * 转JSON
	 *
	 * @param data 需要转换的对象
	 * @return JSON
	 */
	public static String toJson(Object data) {
		ObjectMapper mapper = new ObjectMapper();
		mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"));
		String dataString;
		try {
			dataString = mapper.writeValueAsString(data);
		} catch (JsonProcessingException ee) {
			ee.printStackTrace();
			dataString = "";
		}
		return dataString;
	}

	public static <T> T toObject(String data,Class<T> cls) {
		if(data == null){
			return null;
		}
		ObjectMapper mapper = new ObjectMapper();
		mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"));
		T obj = null;
		try {
			var reader = mapper.readerFor(cls);
			obj = reader.readValue(data);
		} catch (JsonProcessingException ee) {
			ee.printStackTrace();
		}
		return obj;
	}

	public static void writeObjectToResponse(HttpServletResponse response, Object obj) {
		try {
			response.setContentType("application/json");
			response.setCharacterEncoding("utf-8");
			response.getWriter().write(toJson(obj));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static Map<String, Object> jsonParse(String content) throws JsonProcessingException {
		ObjectMapper mapper = new ObjectMapper();
		var reader = mapper.readerForMapOf(Object.class);
		return reader.readValue(content);
	}

	public static String getDBMapCode(Class<?> classType) {
		if (classType.isAnnotationPresent(DBTable.class)) {
			var dbTable = classType.getAnnotation(DBTable.class);
			return dbTable.mapCode();
		}
		return classType.getName();
	}

	/**
	 * 获取两个日期的年份差  公式:one-two
	 *
	 * @param one one
	 * @param two two
	 * @return 差值
	 */
	public static int getYearDifference(Date one, Date two) {
		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy");
		Integer nowYear = Integer.parseInt(dateFormat.format(one));
		Integer oldYear = Integer.parseInt(dateFormat.format(two));
		return nowYear - oldYear;
	}
	public static long dateDiff(String startTime, String endTime, String format) throws Exception {
		//按照传入的格式生成一个simpledateformate对象
		SimpleDateFormat sd = new SimpleDateFormat(format);
		long nd = 1000*24*60*60;//一天的毫秒数
		long nh = 1000*60*60;//一小时的毫秒数
		long nm = 1000*60;//一分钟的毫秒数
		long ns = 1000;//一秒钟的毫秒数
		long diff;
		//获得两个时间的毫秒时间差异
		diff = sd.parse(startTime).getTime() - sd.parse(endTime).getTime();
		long day = diff/nd;//计算差多少天
		long hour = diff%nd/nh;//计算差多少小时
		long min = diff%nd%nh/nm;//计算差多少分钟
		long sec = diff%nd%nh%nm/ns;//计算差多少秒//输出结果
		System.out.println("时间相差："+day+"天"+hour+"小时"+min+"分钟"+sec+"秒。");
		return (day*1440)+(hour*60)+min ;
	}

	public static Field getClassField(Class<?> classType, String fieldName) {
		Field field = null;
		var fields = classType.getDeclaredFields();

		if (fields.length > 0) {
			for (var tf : fields) {
				if (tf.getName().equalsIgnoreCase(fieldName.toLowerCase())) {
					field = tf;
					break;
				}
			}
		}
		return field;
	}

	public static DBField getSerialNoDBField(Class<?> classType) {
		DBField field = null;
		var fields = classType.getDeclaredFields();

		if (fields.length > 0) {
			for (var tf : fields) {
				if (tf.isAnnotationPresent(DBField.class)) {
					field = tf.getAnnotation(DBField.class);
					if (field.isSerialNumber()) {
						break;
					}
				}
			}
		}
		return field;
	}

	public static String getChangeLog(Object object, Map<?, ?> form) throws Exception {

		var targetMethods = object.getClass().getMethods();
		var fields = object.getClass().getDeclaredFields();
		var logList = new ArrayList<String>();
		for (var m : targetMethods) {
			String methodName = m.getName();
			if (!methodName.startsWith("get") && !methodName.startsWith("is")) {
				continue;
			}
			var fieldName = "";
			if (methodName.startsWith("get")) {
				fieldName = methodName.substring(3);
			}
			if (methodName.startsWith("is")) {
				fieldName = methodName.substring(2);
			}
			Field field = getField(fields, fieldName);
			if (field != null) {
				if (form.containsKey(field.getName())) {
					var newValue = form.get(field.getName());
					/*if ("state".equals(field.getName()) && (newValue != null && Integer.parseInt(newValue.toString()) == -1)) {
						continue;
					}*/
					Object oldValue = m.invoke(object);
					DBField log = field.getAnnotation(DBField.class);
					var valueStr = "";
					var oldValueStr = "";
					var format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
					if(newValue !=null) {

						if(newValue instanceof Date) {
							valueStr = format.format(newValue);
						}
						else {
							valueStr = newValue.toString();
						}
					}
					if (oldValue != null) {
						if(oldValue instanceof Date){
							oldValueStr = format.format(oldValue);
						}
						else {
							oldValueStr = oldValue.toString();
						}
					}

					if (!valueStr.equals(oldValueStr)) {
						logList.add(String.format("%s[%s→%s]", log == null ? fieldName : log.disName(),
								oldValue, valueStr
						));
					}

					var setMethod = object.getClass().getMethod(String.format("set%s", fieldName), field.getType());
					try {
						setMethod.invoke(object, parseToTypeValue(valueStr, field.getType()));
					} catch (Exception ee) {
						System.out.println(field.getName() + "__" + field.getType()+"__"+valueStr);
						ee.printStackTrace();
					}
				}

			}
		}
		if (logList.size() > 0) {
			var array = new String[logList.size()];
			return String.join(",", logList.toArray(array));
		}
		return "";
	}

	private static Object parseToTypeValue(String value, Class<?> type) throws ParseException {
		if (type == Long.class) {
			if (!isEmpty(value)) {
				return Long.parseLong(value);
			}
			else
			{
				return null;
			}
		}
		if (type == long.class) {
			if (value == null) {
				return (long) 0;
			} else {
				return Long.parseLong(value);
			}
		}
		if (type == int.class) {
			if (Tl.isEmpty(value)) {
				return 0;
			} else {
				return Integer.parseInt(value);
			}
		}
		if (type == Integer.class) {
			if (Tl.isEmpty(value)) {
				return null;
			} else {
				return Integer.parseInt(value);
			}
		}

		if (type == Short.class) {
			if (value != null) {
				if("".equals(value)){
					return (short)0;
				}else{
					return Short.parseShort(value);
				}
			}
		}
		if (type == short.class) {
			if (value == null) {
				return (short) 0;
			} else {
				return Short.parseShort(value);
			}
		}
		if (type == char.class) {
			if (value != null) {
				return value.toCharArray()[0];
			} else {
				return 0;
			}
		}
		if (type == BigDecimal.class) {
			if (!Tl.isEmpty(value)) {
				var d = Double.parseDouble(value);
				return new BigDecimal(d);
			}
			else
			{
			    return BigDecimal.ZERO;
			}
		}
		if (type == Float.class) {
			if (value != null) {
				return Float.parseFloat(value);
			}
			else
			{
				return null;
			}
		}
		if (type == float.class) {
			if (value == null) {
				return (float) 0;
			} else {
				return Float.parseFloat(value);
			}
		}
		if (type == Double.class) {
			if (value != null) {
				return Double.parseDouble(value);
			}
			else
			{
				return null;
			}
		}
		if (type == double.class) {
			if (value == null) {
				return (double) 0;
			} else {
				return Double.parseDouble(value);
			}
		}
		if (type == Boolean.class) {
			if ("".equals(value)) {
				return false;
			}
			else
			{
				return Boolean.parseBoolean(value.toLowerCase());
			}
		}
		if (type == boolean.class) {
			if (value == null || "".equals(value)) {
				return false;
			} else {
				return Boolean.parseBoolean(value);
			}
		}
		if (type == Date.class) {
			if (!isEmpty(value)) {
				var varStr = value;
				String format = "yyyy-MM-dd HH:mm:ss";
				if (Pattern.matches("^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}:\\d{3}$", varStr)) {
					format = "yyyy-MM-dd HH:mm:ss:SSS";
				} else if (Pattern.matches("^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}$", varStr)) {
					format = "yyyy-MM-dd HH:mm:ss";
				} else if (Pattern.matches("^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}$", varStr)) {
					format = "yyyy-MM-dd HH:mm";
				} else if (Pattern.compile("\\d{4}-\\d{2}-\\d{2}").matcher(varStr).lookingAt()) {
					var m = Pattern.compile("\\d{4}-\\d{2}-\\d{2}").matcher(varStr);
					if (m.find()) {
						varStr = m.group();
						format = "yyyy-MM-dd";
					}
				}
				SimpleDateFormat sdf = new SimpleDateFormat(format);
				return sdf.parse(varStr);
			}
			else
			{
				return null;
			}
		}
		return value;
	}

	public static Field getField(Field[] fields, String fieldName) {
		Field field = null;
		if (fields != null && fields.length > 0) {
			for (var tf : fields) {
				if (tf.getName().equalsIgnoreCase(fieldName)) {
					field = tf;
					break;
				}
			}
		}
		return field;
	}

	public static boolean filedExists(Field[] fields, String fieldName) {
		var flag = false;
		if (fields != null && fields.length > 0) {
			for (var tf : fields) {
				if (tf.getName().equalsIgnoreCase(fieldName)) {
					flag = true;
					break;
				}
			}
		}
		return flag;
	}

	public static List<String> except(String[] a, String[] b) {
		var list = new ArrayList<String>();
		for (var i : a) {
			boolean flag = false;
			for (var j : b) {
				if (j.equals(i)) {
					flag = true;
					break;
				}
			}
			if (!flag) {
				list.add(i);
			}
		}
		return list;
	}

	public static boolean filedExists(Class<?> type,String field){
		return filedExists(type.getDeclaredFields(),field);
	}

	public static String getRawMaterialFullName(RawMaterialInfoEntity entity) {
		List<String> nameList = new ArrayList<>();
		if (!Tl.isEmpty(entity.getRmName())) {
			nameList.add(entity.getRmName());
		}
		if (!Tl.isEmpty(entity.getRmSpec())) {
			nameList.add(entity.getRmSpec());
		}
		if (!Tl.isEmpty(entity.getBrand())) {
			nameList.add(entity.getBrand());
		}
		return String.join("/", nameList.toArray(new String[0]));
	}

	public static String charCopyToLeft(String source,int length,String charStr){
		if(length==0){
			return source;
		}
		else
		{
			return charCopyToLeft(String.format("%s%s",charStr,source),(length -1),charStr);
		}

	}

	public static List<? extends IParent> toTreeSelect(List<? extends IParent> list, String parentSN){
		List<IParent> newList = new ArrayList<>();
		if(isEmpty(parentSN)){
			var s = list.stream().filter(t->t.getLayer()==0).toList();
			for (IParent obj : s) {
				newList.add(obj);
				newList.addAll(toTreeSelect(list, obj.getSn()));
			}
		}
		else
		{
			var s = list.stream().filter(t->parentSN.equals(t.getParentSN())).toList();
			if(s.size()>0){
				for (IParent obj : s) {
					newList.add(obj);
					newList.addAll(toTreeSelect(list, obj.getSn()));
				}
			}
		}
		return newList;
	}

	public static List<Map<String,Object>> toTreeList(List<? extends IParent> list, String parentSN) {
		List<Map<String,Object>> newData = new ArrayList<>();
		List<? extends IParent> nodes;

		if (isEmpty(parentSN)) {
			nodes = list.stream().filter(t -> t.getLayer() == 0).collect(Collectors.toList());
		} else {
			nodes = list.stream().filter(t -> !isEmpty(t.getParentSN()) && t.getParentSN().equals(parentSN)).collect(Collectors.toList());
		}
		if ( nodes.size() > 0) {
			nodes.forEach(t -> {
				var map = toMap(t);
				var child = toTreeList(list, t.getSn());
				map.put("childrens", child);
				newData.add(map);
			});
		}
		return newData;
	}

	private static  List<OptionModel> areaToTreeList(List<AreaEntity> list, String parentSN){
		if(list.stream().anyMatch(t->t.getParentSN().equals(parentSN))) {
			return list.stream().filter(t -> t.getParentSN().equals(parentSN)).map(t -> new OptionModel(t.getSn(), t.getAreaName(), areaToTreeList(list, t.getSn()))).collect(Collectors.toList());
		}
		return null;
	}
	public static List<OptionModel> areaToOptionTreeList(List<AreaEntity> list) {
		return areaToTreeList(list,"0");
	}

	public static String getFieldName(Field field) {
		return field.getName().replace("_", "");
	}

	public static Object getFieldValue(Field field,Object obj) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

		String fieldName = getFieldName(field);
		var classType = obj.getClass();
		String firstLetter = fieldName.substring(0, 1).toUpperCase();
		var methodHead = "get";
		var ftype = field.getType();
		if (ftype.getName().equals("boolean")) {
			methodHead = "is";
		}
		// 获得和属性对应的getXXX()方法的名字
		String getMethodName = methodHead + firstLetter + fieldName.substring(1);
		// 获得和属性对应的getXXX()方法
		Method getMethod = classType.getMethod(getMethodName);
		// 调用原对象的getXXX()方法
		return getMethod.invoke(obj);
	}

	/**
	 * 实体转映射
	 *
	 * @return Map结构实体类
	 */
	public static Map<String, Object> toMap(Object obj) {
		Map<String, Object> map = new HashMap<>();
		var classType = obj.getClass();
		Field[] fields = classType.getDeclaredFields();

		for (var field : fields) {
			try {
				String fieldName = getFieldName(field);
				String firstLetter = fieldName.substring(0, 1).toUpperCase();
				var methodHead = "get";
				var ftype = field.getType();
				if (ftype.getName().equals("boolean")) {
					methodHead = "is";
				}
				// 获得和属性对应的getXXX()方法的名字
				String getMethodName = methodHead + firstLetter + fieldName.substring(1);
				// 获得和属性对应的getXXX()方法
				Method getMethod = classType.getMethod(getMethodName);
				// 调用原对象的getXXX()方法
				Object value = getMethod.invoke(obj);
				map.put(fieldName, value);
			} catch (Exception ee) {
				ee.printStackTrace();
			}
		}
		return map;
	}


	/**
	 * Object转BigDecimal类型
	 *
	 * @param value 要转的object类型
	 * @return 转成的BigDecimal类型数据
	 */
	public static BigDecimal getBigDecimal(Object value) {
		BigDecimal ret = new BigDecimal(0);
		if (value != null) {
			if (value instanceof BigDecimal) {
				ret = (BigDecimal) value;
			} else if (value instanceof String) {
				ret = new BigDecimal(value.toString());
			} else if (value instanceof BigInteger) {
				ret = new BigDecimal((BigInteger) value);
			} else if (value instanceof Number) {
				ret = new BigDecimal(value.toString());
			} else {
				throw new ClassCastException("Not possible to coerce [" + value + "] from class " + value.getClass() + " into a BigDecimal.");
			}
		}
		return ret;
	}
    public static Date strToDate(String dateStr){
		var regex1 = "^[1-9]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])\\s+(20|21|22|23|[0-1]\\d):[0-5]\\d:[0-5]\\d$";
		var regex2 = "^[1-9]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])\\s+(20|21|22|23|[0-1]\\d):[0-5]\\d$";
		var regex3 = "^[1-9]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$";
        try {
			if(Pattern.matches(regex1,dateStr)){
				return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(dateStr);
			}
			if(Pattern.matches(regex2,dateStr)){
				return new SimpleDateFormat("yyyy-MM-dd HH:mm").parse(dateStr);
			}
			if(Pattern.matches(regex3,dateStr)){
				return new SimpleDateFormat("yyyy-MM-dd").parse(dateStr);
			}
			return new Date();
        } catch (ParseException e) {
            e.printStackTrace();
            return new Date();
        }
    }
	public static Date strToDate(String dateStr,String pattern){
		var format = new SimpleDateFormat(pattern);
		try {
			return format.parse(dateStr);
		} catch (ParseException e) {
			e.printStackTrace();
			return new Date();
		}
	}

    public static Object getFieldValue(String value,Field field) throws ParseException {
        var cls = field.getType();
        Object val = value;
        if(cls == Date.class){

            if(value.length()==10)
            {
                var format = new SimpleDateFormat("yyyy-MM-dd");
                val = format.parse(value);
            }
            else if(value.length() == 16)
            {
                var format = new SimpleDateFormat("yyyy-MM-dd HH:mm");
                val = format.parse(value);
            }
            else
            {
                val = new Date();
            }
        }
        else if(cls== int.class){
            val = Integer.parseInt(value);
        }
        else if(cls == boolean.class){
            val = Boolean.parseBoolean(value)?1:0;
        }
        else if(cls == float.class || cls == BigDecimal.class || cls == double.class){
            val = Float.parseFloat(value);
        }
        return  val;
    }

	/**
	 *  OADate 时间转化
	 * @param d 值
	 * @return 时间
	 * @throws ParseException 转化异常
	 */
	public static Date convertFromOADate(double d) throws ParseException {
		double  mantissa = d - (long) d;
		double hour = mantissa*24;
		double min =(hour - (long)hour) * 60;
		double sec=(min- (long)min) * 60;


		SimpleDateFormat myFormat = new SimpleDateFormat("dd MM yyyy");
		Date baseDate = myFormat.parse("30 12 1899");
		Calendar c = Calendar.getInstance();
		c.setTime(baseDate);
		c.add(Calendar.DATE,(int)d);
		c.add(Calendar.HOUR,(int)hour);
		c.add(Calendar.MINUTE,(int)min);
		c.add(Calendar.SECOND,(int)sec);

		return c.getTime();
	}
	/**
	 * @param start 开始时间
	 * @param end 结束时间
	 * @return 时间集合
	 */
	public  static List<Date> getBetweenDates(Date start, Date end) {
		List<Date> result = new ArrayList<>();
		Calendar tempStart = Calendar.getInstance();
		tempStart.setTime(start);
		tempStart.add(Calendar.DAY_OF_YEAR, 1);
		Calendar tempEnd = Calendar.getInstance();
		tempEnd.setTime(end);
		while (tempStart.before(tempEnd)) {
			tempStart.set(Calendar.HOUR_OF_DAY, 0);
			tempStart.set(Calendar.MINUTE, 0);
			tempStart.set(Calendar.SECOND, 0);
			result.add(tempStart.getTime());
			tempStart.add(Calendar.DAY_OF_YEAR, 1);
		}
		return result;
	}

	public static boolean getBool(Object value) {
		if (value != null) {
			if (value instanceof Boolean) {
				return  (Boolean) value;
			} else if (value instanceof String) {
				return Boolean.parseBoolean(value.toString());
			}
		}
		return false;
	}
	public static boolean getBool(Object value,boolean defValue) {
		if (value != null) {
			if (value instanceof Boolean) {
				return  (Boolean) value;
			} else if (value instanceof String) {
				return Boolean.parseBoolean(value.toString());
			}
		}
		return defValue;
	}

	public static String getString(Object value) {
		if(value==null){
			return  null;
		}
		return value.toString();
	}

    public static int getInteger(Object value) {
		if(value==null){
			return 0;
		}
		return Integer.parseInt(value.toString());
    }
	public static int getInteger(Object value,int def) {
		if(value==null){
			return def;
		}
		return Integer.parseInt(value.toString());
	}

	public static Map<String,String> parseTaskParam(String params){
		Map<String,String> data = new HashMap<>();
		if(!isEmpty(params)) {
			var paramArray = params.split(";");
			for (var strVal:paramArray){
				if(!isEmpty(strVal)) {
					var svArray = strVal.split(":");
					if(svArray.length==2) {
						data.put(svArray[0], svArray[1]);
					}
				}
			}
		}
		return data;
	}

	public static short getShort(Object value) {
		if(value==null){
			return (short) 0;
		}
		return Short.parseShort(value.toString());
	}
	public static short getShort(Object value, short def) {
		if(value==null){
			return def;
		}
		return Short.parseShort(value.toString());
	}

	public static String getDateString(Date time, String s) {
		SimpleDateFormat sdf = new SimpleDateFormat(s);
		return  sdf.format(time);
	}
	public static String getDateString(Date time) {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
		return  sdf.format(time);
	}

	public static String getRandomCode(int length) {
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < length; i++) {
			sb.append(getRandomChar());
		}
		return  sb.toString();
	}

	private static char getRandomChar(){
		String str = "ASDFGHJKLQWERTYUIOPZXCVBNM123456789";
		Random r = new Random();
		return str.charAt(r.nextInt(str.length()));
	}

	public static Double timeToDay(long time){
		return timeToHour(time) / 24;
	}
	public static Double timeToHour(long time){
		return timeToMinute(time) /60;
	}
	public static Double timeToMinute(long time){
		return timeToSecond(time) / 60;
	}
	public static Double timeToSecond(long time){
		return Double.parseDouble((time / 1000)+ "");
	}
	public static List<Map<String, Object>> parseToMapList(Object obj){
		List<Map<String,Object>> mapList = new ArrayList<>();
		if(obj instanceof List<?> list){
			if(list.size()>0){
				for(var i : list){
					if(i instanceof Map<?,?> map){
						Map<String,Object> item = new HashMap<>();
						for(var key : map.keySet()){
							item.put(key.toString(),map.get(key));
						}
						mapList.add(item);
					}
				}
			}
		}
		return mapList;
	}
	public static List<String> parseSNList(Object obj){
		List<String> orderSnList = new ArrayList<>();
		if (obj instanceof List<?> snList) {
			for (var s : snList) {
				orderSnList.add(s.toString());
			}
		}
		return orderSnList;
	}
}
